package org.spdata.tsdb.entity;

import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.bson.Document;
import org.spdata.tsdb.coder.DefaultDpsCoder;
import org.spdata.tsdb.entity.Metric.ValueType;

import com.mongodb.BasicDBObject;


/**
 * 测点在某个小时的采样数据包
 * @author Administrator
 *
 */
public class DpsPack extends Entity<Long>{
	private Long id;
	private Map<Integer,Number> dps = null;
	private Integer metricId =  null;
	private String metricName;
	private Map<String,String> tags;
	private Long hour = null;
	private Summary summary;
	private Date timestamp;

	
	@Override
	public int hashCode() {
		return this.id.hashCode();
	}

	@Override
	public boolean equals(Object obj) {
		if(obj==null)
			return false;
		if(obj instanceof DpsPack)
			return this.id.equals(((DpsPack)obj).getId());
		else
			return false;
	}
	public DpsPack() {
		
	}

	@Override
	public Long getId() {
		return id;
	}
	
	public String getMetricName() {
		return metricName;
	}

	public void setMetricName(String metricName) {
		this.metricName = metricName;
	}


	public Map<String, String> getTags() {
		return tags;
	}

	public void setTags(Map<String, String> tags) {
		this.tags = tags;
	}

	public Date getTimestamp() {
		return timestamp;
	}


	public void setTimestamp(Date timestamp) {
		this.timestamp = timestamp;
	}

	public Map<Integer, Number> getDps() {
		return dps;
	}

	public void setDps(Map<Integer, Number> dps) {
		this.dps = dps;
	}

	public Integer getMetricId() {
		return metricId;
	}

	public void setMetricId(Integer metricId) {
		this.metricId = metricId;
	}

	public Long getHour() {
		return hour;
	}

	public void setHour(Long hour) {
		this.hour = hour;
	}

	public void setId(Long id) {
		this.id = id;
	}
	
	public Summary getSummary() { 
		return summary;
	}

	public void setSummary(Summary summary) {
		this.summary = summary;
	}
	

	public DpsPack(long hour,Integer metricId,String metricName,Map<String,String> tags,Map<Integer,Number> dps) {
		this.id = (hour<<32) + metricId;
		this.hour = hour;
		this.metricId = metricId;
		this.metricName = metricName;
		this.tags = tags;
		this.dps = dps;
	}

	public DpsPack(long id,Map<Integer,Number> dps){
		this.id = id;
		this.metricId = (int)(id & 0x00000000ffffffffL);
		this.hour = id>>32;
		this.dps = dps;
	}
	
	public DpsPack(long id){
		this.id = id;
		this.metricId = (int)(id & 0x00000000ffffffffL);
		this.hour = id>>32;
	}
	
	public DpsPack(long hour,Integer metricIndex,Map<Integer,Number> dps){
		this.id = (hour<<32) + metricIndex;
		this.dps =  dps;
		this.hour = hour;
		this.metricId = metricIndex;
	}
	
	public void addDataPoint(int secondInHour,Number value){
		this.dps.put(secondInHour, value);
	}

	@Override
	public Document toDocument() throws Exception {
		if(this.timestamp==null) {
			long hour = this.getHour();
			long time = hour * 3600000;
			this.timestamp = new Date(time);
		}
			
		Document document = new Document();
		document.append("_id", id);
		document.append("timestamp", this.timestamp);
		if(this.summary==null) {
			this.doSummary();
			BasicDBObject summary = new BasicDBObject();			
			summary.append("begin", this.summary.getBegin());
			summary.append("end", this.summary.getEnd());
			summary.append("max", this.summary.getMax());
			summary.append("min", this.summary.getMin());
			summary.append("avg", this.summary.getAvg());
			document.append("summary", summary);
		}
		document.append("dps", this.toBytes());
		return document;
	}
	
	public void setData(byte[] data,ValueType type) {
		/*
		this.dps = new LinkedHashMap<Integer,Number>();
		for(int i=0;i<data.length;i+=6) {
			Integer secondInHour = ByteUtils.changeBytesToInt(data[i],data[i+1]);
			Integer value = ByteUtils.changeBytesToInt(data[i+2],data[i+3],data[i+4],data[i+5]);
			Number v = null;
			if(type.equals(ValueType.floatValue)) {
				v = Float.intBitsToFloat(value);
			}
			this.dps.put(secondInHour, v);
		}*/
		DefaultDpsCoder coder = new DefaultDpsCoder(type);
		DpsPack pack = coder.decode(data);
		this.setDps(pack.getDps());
	}
	
	public byte[] toBytes() throws Exception {
		//非压缩编码
		/*
		ByteArrayOutputStream output = new ByteArrayOutputStream(4096);
		Iterator<Entry<Integer,Number>> iter = dps.entrySet().iterator();
		Boolean isFloat = null;
		while(iter.hasNext()){
			Entry<Integer,Number> entry = iter.next();
			Integer secondInHour = entry.getKey();
			Number data = entry.getValue();
			
			output.write(ByteUtils.changeIntTo2Bytes(secondInHour));
			
			if(isFloat==null) {
				isFloat = data instanceof Float;
			}
			if(isFloat)
				output.write(ByteUtils.changeIntTo4Bytes(Float.floatToIntBits((Float)data)));
			else{
				output.write(ByteUtils.changeIntTo4Bytes((Integer)data));
			}
		}
		byte[] bytes = output.toByteArray();
		return bytes;
		*/
		//压缩编码
		DefaultDpsCoder coder = new DefaultDpsCoder(null);
		byte[] encoded = coder.encode(this);
		return encoded;
	}

	public DpsPack doSummary(){
		if(dps==null || dps.size()==0)
			return null;
		Number firstValue = dps.entrySet().iterator().next().getValue();
		if(firstValue instanceof Float ==false)
			return this;
		Float avg =null;
		Float max =null;
		Float min =null;
		Float begin =null;
		Float end = null;
		Float sum = 0F;
		int count =0;
		Set<Entry<Integer,Number>> set = dps.entrySet();
		for(Entry<Integer,Number> entry:set){
			Float value = (Float)entry.getValue();
			sum = sum+value;
			if(count==0){
				max = value;
				min = value;
				begin = value;
				end = value;
			}else{
				if(value>max)
					max = value;
				if(value<min)
					min = value;
				end = value;
			}
			count ++;
		}
		avg = sum/count;
		summary = new Summary();
		summary.setAvg(avg);
		summary.setBegin(begin);
		summary.setEnd(end);
		summary.setMax(max);
		summary.setMin(min);
		return this;
	}
	
	public void subDps(long startSecond,long endSecond) {		
		if(this.dps!=null && this.dps.size()>0){
			Iterator<Entry<Integer,Number>> iter = this.dps.entrySet().iterator();
			while(iter.hasNext()){
				Entry<Integer,Number> entry = iter.next();
				Integer secondInHour = entry.getKey();
				long second = this.hour*3600+secondInHour;
				if(second<startSecond || second>endSecond)
					iter.remove();
			}
		}
	}
	

	
	
}
